// 对于传统的 JavaScript 程序我们会使用函数和基于原型的继承来创建可重用的组件，
// 但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手，
// 因为他们用的是`基于类的继承`并且对象是由类构建出来的。
// 从 ECMAScript 2015，也就是 ES6 开始， JavaScript 程序员将能够使用基于类的面向对象的方式。
// 使用 TypeScript，我们允许开发者现在就使用这些特性，
// 并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行，而不需要等到下个 JavaScript 版本。


// 类是对象的状态(属性)和行为(方法)的具体(描述)

class  Greeter{
    // 类的成员-成员属性
    message:string
    // 构造器
    constructor(message:string){
        this.message = message
    }
    // 类的方法-成员方法
    greet() {
        return "hello" +  this.message
    }
}


//实例化对象
let  gt = new Greeter("world")

console.log(gt.greet());



/* 
    2.class继承 多态（重构）封装性
*/


class Animal {
    public name:string
    private hoby:string
    constructor(name:string,hoby:string){
        this.name = name
        this.hoby = hoby
    }
    public run(distance:number=0){
        console.log(`${this.name} run ${distance}m`);
        
    }
}


class Cat extends Animal {
    age:number = 3
    sex:string = "male"
    run(distance:number=7){
        console.log(`cat jumping...`); 
        // console.log(this.hoby);
        super.run(distance)
    }
}


class Pig extends Animal {
    run(distance:number=3){
        console.log(`pig jumping...`); 
        // console.log(this.hoby);
        
        super.run(distance)
    }
}

let c = new Cat("黑猫警长","抓老鼠")
console.log(c.name);
// console.log(c.hoby);
// 子类重写父类方法 覆盖
console.log(c.run(6));
let p1 = new Pig("猪八戒","看美女")
console.log(p1.name);
// console.log(p1.hoby);

// 子类重写父类方法  覆盖
console.log(p1.run(2));


/* 
    3.class封装性（公共，私有与受保护的修饰符）
    public:
        默认值, 公开的外部也可以访问
    protected:
        受保护的
        类内部和子类可以访问
    private: 
        只能类内部可以访问

*/


/* 
    4.class readonly 修饰符
    你可以使用 `readonly` 关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
*/


// class Persons{
//     readonly name:string = "nba"
//     constructor(name:string){
//         this.name = name
//     }

// }

// let p2 = new Persons("cba")
// console.log(p2.name);
// p2.name = "www"


// 参数属性通过给构造函数参数前面添加一个访问限定符来声明

class Persons{
    // 参数属性   
    constructor(readonly name:string){
        this.name = name
    }

}

let p2 = new Persons("cba")
console.log(p2.name);

/* 
    5.class 存取器
    `TypeScript` 支持通过 `getters/setters` 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
*/

class  Per{
    firstName: string = "诸葛"
    lastName: string = "孔明"
    get  fullName(){
        return this.firstName + "-" + this.lastName
    }
    set fullName(value){
        console.log(value);
       let names = value.split("-")
       this.firstName = names[0]
       this.lastName = names[1]
    }
}

let pp = new Per()
console.log(pp.fullName);

pp.firstName = "孤独"
pp.lastName = "求败"
console.log(pp.fullName);

pp.fullName = "东方-不败"
console.log(pp.firstName,pp.lastName);


/* 
    6.class 静态属性
    静态属性, 是类对象的属性
    非静态属性, 是类的实例对象的属性
    到目前为止，我们只讨论了类的实例成员，那些仅当类被实例化的时候才会被初始化的属性。 
    我们也可以创建类的静态成员，这些属性存在于类本身上面而不是类的实例上。 
    在这个例子里，我们使用 `static` 定义 `origin`，因为它是所有网格都会用到的属性。 
    每个实例想要访问这个属性的时候，都要在 `origin` 前面加上类名。
     如同在实例属性上使用 `this.xxx` 来访问属性一样，这里我们使用 `Grid.xxx` 来访问静态属性。
*/

class  Per2 {
    name1:string = "A"
    static name2:string = "B"
}

console.log(new Per2().name1);
console.log(Per2.name2);

// 静态属性, 是类对象的属性  保存在共享内存种  不在堆内存种


/* 
    7.class 抽象类
    抽象类做为其它派生类的基类使用。 它们不能被实例化。不同于接口，抽象类可以包含成员的实现细节。 
    abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。

    不能创建实例对象, 只有实现类才能创建实例
    可以包含未实现的抽象方法
*/

// 抽象类做为其它派生类的基类使用。 它们不能被实例化
abstract class Animals{
    name:string = "高级动物"
    run(){
        console.log("run...");
    }
    abstract cry()
}


class Dog extends Animals{
    cry() {
       console.log("汪汪");
    }
}

let d = new Dog()
console.log(d.name);
console.log(d.run());
console.log(d.cry());
